﻿using System.Threading;

namespace XYCAM.Generator.CAD;

/// <summary>
/// 命令属性生成器,读取继承XyCommandObject的类型,且标记了XyCommand特性的方法,生成对应的命令数据类型的属性
/// </summary>
[Generator(LanguageNames.CSharp)]
public sealed class XyCommandAttributeGenerator : IIncrementalGenerator
{
    /// <summary>
    /// 被识别的基类类型名
    /// </summary>
    private const string AllowBaseClassName = ConstStrings.AllowBaseClassName;

    /// <summary>
    /// 被识别的类的命名空间
    /// </summary>
    private const string AllowBaseObjectNameSpace = ConstStrings.AllowBaseObjectNameSpace;

    /// <summary>
    /// 方法特性
    /// </summary>
    private const string MethodAttrbuteName = "XyCommand";

    /// <summary>
    /// 生成文件的后缀名
    /// </summary>
    private const string Suffix = ConstStrings.Suffix;

    /// <summary>
    /// 设置CAD回调函数的方法名
    /// </summary>
    private const string SetCommandCallbackMethod = "SetCommandCallback";


    /// <summary>
    /// 数据类中设置命令重复时回调函数的方法名
    /// </summary>
    private const string SetOnCommandRepeatCallbackMethod = "SetOnCommandRepeatCallback";

    /// <summary>
    /// 实例中已实现的命令重复时回调函数
    /// </summary>
    private const string OnCommandRepeatMethod = "OnCommandRepeat";

    /// <summary>
    /// 实例中的命令集合私有字段
    /// </summary>
    private const string CommandsField = "m_Commands";

    /// <summary>
    /// 实例中的命令集合私有字段
    /// </summary>
    private const string CommandsProperty = "Commands";

    /// <summary>
    /// 原始命令的前缀标识
    /// </summary>
    private const string CommandFalg = "xy_";


    /// <summary>
    /// 初始化操作
    /// </summary>
    /// <param name="context"></param>
    public void Initialize(IncrementalGeneratorInitializationContext context)
    {
        ////启动调试器
        //if (!Debugger.IsAttached)
        //{
        //    Debugger.Launch();
        //}
        // 注册源代码输出，将在编译期间生成并输出源代码
        context.RegisterSourceOutput(
            context.SyntaxProvider.CreateSyntaxProvider(
                 //前置判断
                 Predicate,
                //数据转换
                TransformContext
                ).Collect(),
            //生成代码
            Create);
    }

    /// <summary>
    /// 断言类型节点
    /// </summary>
    /// <param name="node"></param>
    /// <returns></returns>
    static bool Predicate(SyntaxNode node, CancellationToken _)
    {
        //判断是否为类型语义
        if (node is not ClassDeclarationSyntax
            {
                //访问修饰符不能为空
                Modifiers: SyntaxTokenList
                {
                    Count: > 0,
                } modifiers,
                //获取基类列表,并且不能为空,就是说必须为派生类,所以它也不会为静态类
                BaseList.Types: SeparatedSyntaxList<BaseTypeSyntax>
                {
                    Count: > 0,
                } baseTypes,
            })
        {
            return false;
        }

        //类型的修饰符必须包含Partial
        if (!modifiers.Any(SyntaxKind.PartialKeyword))
        {
            return false;
        }
        //获取基类的名称,第一个才是类型,后面或许还有接口
        var baseTypeName = baseTypes.First().ToString();
        if (GetGenerictypeNameStruct(baseTypeName) is not (var typeName, var generictypeName))
        {
            return false;
        }
        //分割泛型参数,只能有一个参数
        if (generictypeName.Contains(','))
        {
            return false;
        }

        //判断是否与指定类型一致
        return typeName.Equals(AllowBaseClassName);
    }

    /// <summary>
    /// 数据转换
    /// </summary>
    /// <param name="gasc"></param>
    /// <param name="_"></param>
    /// <returns></returns>
    static ClassGatheredData? TransformContext(GeneratorSyntaxContext gasc, CancellationToken _)
    {
        //语义节点必须为方法
        if (gasc.Node is not ClassDeclarationSyntax
            {
                //获取基类列表,并且不能为空
                BaseList.Types: SeparatedSyntaxList<BaseTypeSyntax>
                {
                    Count: > 0
                } baseTypes,
                //获取类型的父语义
                Parent: var parent,
            } classDeclarationSyntax)
        {
            return null;
        }
        string @namespace;

        //获取命令空间语义
        if (parent is NamespaceDeclarationSyntax namespaceDeclarationSyntax)
        {
            @namespace = namespaceDeclarationSyntax.Name.ToString();
        }
        else if (parent is FileScopedNamespaceDeclarationSyntax fileScopedNamespaceDeclarationSyntax)
        {
            @namespace = fileScopedNamespaceDeclarationSyntax.Name.ToString();
        }
        else
        {
            return null;
        }

        //必须为类型
        if (!classDeclarationSyntax.IsKind(SyntaxKind.ClassDeclaration))
        {
            return null;
        }

        //获取类型名称
        var typeName = classDeclarationSyntax.Identifier.Text;

        //获取基类的名称
        var baseTypeName = baseTypes.Last().ToString();

        //必须包含泛型参数,但是我不知道怎么获取,目前只能用字符串分割了
        var index = baseTypeName.IndexOf("<");
        if (index < 0) return null;
        //var generictypeName = baseTypeName[index..];
        var generictypeName = baseTypeName.Substring(index, baseTypeName.Length - index);

        generictypeName = generictypeName.TrimStart('<');
        generictypeName = generictypeName.TrimEnd('>');

        //去除泛型列表
        //baseTypeName = baseTypeName[..index];
        baseTypeName = baseTypeName.Substring(0, index);


        var methodDatas = new List<MethodGatheredData>();
        var childNodes = classDeclarationSyntax.ChildNodes();

        foreach (var child in childNodes)
        {
            if (MethodNodePredicate(child) is MethodGatheredData methodGatheredData and not null)
            {
                methodDatas.Add(methodGatheredData);
            }
        }

        if (methodDatas.Count == 0) return null;

        return new ClassGatheredData(typeName, baseTypeName, generictypeName, @namespace, methodDatas);
    }


    /// <summary>
    /// 生成源码
    /// </summary>
    /// <param name="spc"></param>
    /// <param name="data"></param>
    private void Create(SourceProductionContext spc, ImmutableArray<ClassGatheredData?> data)
    {
        // 遍历收集到的数据
        foreach (var classGatheredData in data)
        {
            if (classGatheredData is null) continue;

            var @namespace = classGatheredData.Namespace;
            var typeName = classGatheredData.TypeName;
            var baseTypeName = classGatheredData.BaseTypeName;
            //完整的泛型类名称
            var generictypeName = classGatheredData.GenerictypeName;

            // 获取命名空间字符串
            var namespaceString = @namespace;

            var hintName = $"{typeName}{Suffix}";

            StringBuilder propertysBuilder = new StringBuilder();
            StringBuilder methodBuilder = new StringBuilder();
            foreach (var methodData in classGatheredData.MethodGatheredDatas)
            {
                string fristStr = methodData.Name[0].ToString();
                //var name = methodData.Name[1..];
                var name = methodData.Name.Substring(1, methodData.Name.Length - 1);
                //生成字段的名称
                var field = $"m_{fristStr.ToLower()}{name}Command";
                //生成属性的名称
                var property = fristStr.ToUpper() + name + "Command";

                //生成属性..
                string propertyString = $$"""
						private {{generictypeName}}? {{field}};
						public {{generictypeName}} {{property}}
						{
							get
							{
								if ({{field}} is null)
								{
									{{field}} = new {{generictypeName}}("{{CommandFalg + methodData.Name}}");
									{{field}}.{{SetCommandCallbackMethod}}({{methodData.Name}});
									{{field}}.{{SetOnCommandRepeatCallbackMethod}}({{OnCommandRepeatMethod}});
								}
								return {{field}};
							}
						}
						
					""";
                propertysBuilder.AppendLine(propertyString);

                //往方法中添加字符
                string methodString = $$"""
									{{CommandsField}}.Add({{property}});
					""";
                methodBuilder.AppendLine(methodString);
            }

            var sourceString = $$"""
				// <auto-generated/>
				
				#nullable enable
				namespace {{namespaceString}};
				
				partial class {{typeName}} : {{AllowBaseObjectNameSpace}}.{{baseTypeName}}<{{generictypeName}}>
				{
				
					private List<{{generictypeName}}>? {{CommandsField}};
					
					/// <summary>
					/// 当前实例的命令集合
					/// <summary>
					public List<{{generictypeName}}> {{CommandsProperty}}
					{
						get
						{
							if ({{CommandsField}} is null)
							{
								{{CommandsField}} = new List<{{generictypeName}}>();
				{{methodBuilder}}
							}
							return {{CommandsField}};
						}
					}
				{{propertysBuilder}}
				}
				""";

            // 添加生成的源代码
            spc.AddSource(hintName, sourceString);
        }
    }


    /// <summary>
    /// 断言方法语义
    /// </summary>
    /// <param name="node"></param>
    /// <returns></returns>
    private static MethodGatheredData? MethodNodePredicate(SyntaxNode node)
    {
        if (node is not MethodDeclarationSyntax methodDeclarationSyntax) return null;

        //必须包含特定的特性名称
        if (!methodDeclarationSyntax.AttributeLists.Any(list => list.Attributes.Any(a => a.Name.ToString() == MethodAttrbuteName)))
        {
            return null;
        }

        //获取返回类型
        var returnType = methodDeclarationSyntax.ReturnType.ToString();
        //返回值必须为Void
        if (returnType is not "void") return null;


        //获取方法名称
        var methodName = methodDeclarationSyntax.Identifier.Text;

        #region 组合出方法的完整字符

        ////获取方法的参数列表
        //StringBuilder stringBuilder = new StringBuilder();
        //foreach (var p in methodDeclarationSyntax.ParameterList.Parameters)
        //{
        //	stringBuilder.Append($"{p.Type} {p.Identifier.Text} ,");
        //}
        //var parameters = stringBuilder.ToString()[..^2];

        ////组合为方法全名
        //string methodSting = $"{methodDeclarationSyntax.Modifiers} {returnType} {methodName}({parameters})";

        #endregion

        return new MethodGatheredData(methodName);
    }

    /// <summary>
    /// 获取基类的字符和泛型参数字符
    /// </summary>
    /// <param name="baseTypeName"></param>
    /// <returns></returns>
    private static (string TypeName, string GenericTypeNames)? GetGenerictypeNameStruct(string baseTypeName)
    {
        //必须包含泛型参数,但是我不知道怎么获取,目前只能用字符串分割了
        var indexStart = baseTypeName.IndexOf("<");
        if (indexStart < 0) return null;

        var indexEnd = baseTypeName.IndexOf(">");
        if (indexEnd < 0) return null;

        //获取去除了泛型集合的类型名
        //var typeName = baseTypeName[..indexStart];

        var typeName = baseTypeName.Substring(0, indexStart);


        indexStart++;
        //获取泛型参数集合
        //var GenericTypeNames = baseTypeName[indexStart..indexEnd];
        var GenericTypeNames = baseTypeName.Substring(indexStart, indexEnd - indexStart);

        return (typeName, GenericTypeNames);
    }

    /// <summary>
    /// 方法名称
    /// </summary>
    /// <param name="Name">方法名</param>
    sealed record MethodGatheredData(string Name);

    /// <summary>
    /// 类型数据集合
    /// </summary>
    /// <param name="TypeName">类名</param>
    /// <param name="BaseTypeName">父类名称</param>
    /// <param name="GenerictypeName">泛型名称</param>
    /// <param name="Namespace">命名空间</param>
    /// <param name="MethodGatheredDatas">方法意义数据集合</param>
    sealed record ClassGatheredData(
        string TypeName,
        string BaseTypeName,
        string GenerictypeName,
        string Namespace,
        List<MethodGatheredData> MethodGatheredDatas
        );


}



